using System;
using System.Drawing;
using System.Drawing.Design;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace ComponentGo.Forms.Design
{
	
	class FlagsEditor : UITypeEditor {
		internal IWindowsFormsEditorService edSvc = null;
		public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
			if(context == null || context.Instance == null || provider == null)
				return value;
			edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
			if(edSvc == null) return value;
			FlagsEditorForm form = new FlagsEditorForm(this);
			form.EditValue = value;
			edSvc.DropDownControl(form);
			value = form.EditValue;
			form.Dispose();
			edSvc = null;
			return value;
		}
		public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
			if(context != null && context.Instance != null)
				return UITypeEditorEditStyle.DropDown;
			return base.GetEditStyle(context);
		}

		internal class FlagsEditorForm : Form {

			internal class AttributesListBox : CheckedListBox {
				int lockUpdate = 0;
				public AttributesListBox() {
					SetStyle(ControlStyles.AllPaintingInWmPaint, true);
				}
				public void ListBoxBeginUpdate() {
					BeginUpdate();
					lockUpdate++;
				}
				public void ListBoxEndUpdate() {
					EndUpdate();
					if(--lockUpdate == 0)
						Invalidate();
				}
				protected override void OnPaint(PaintEventArgs e) {
					if(lockUpdate != 0) return;
					base.OnPaint(e);
				}
			}

			private FlagsEditor mainEditor;
			private AttributesListBox listBox;
			private object editValue, originalValue;
			int lockCheckUpdate;
			public FlagsEditorForm(FlagsEditor editor) {
				lockCheckUpdate = 0;
				mainEditor = editor;
				this.StartPosition = FormStartPosition.WindowsDefaultBounds;
				MaximizeBox = false;
				MinimizeBox = false;
				FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
				TopLevel = false;
				ShowInTaskbar = false;
				TopMost = true;
				this.Font = Control.DefaultFont;
				listBox = new AttributesListBox();
				listBox.Font = Control.DefaultFont;
				listBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
				listBox.Dock = DockStyle.Fill;
				listBox.ItemCheck += new ItemCheckEventHandler(listBox_ItemCheckEventHandler);
				this.ClientSize = new Size(0,listBox.ItemHeight * 7);
				Controls.Add(listBox);
			}
			protected override bool ProcessDialogKey(Keys keyData) {
				if(keyData == Keys.Enter) {
					mainEditor.edSvc.CloseDropDown();
					return true;
				}
				if(keyData == Keys.Escape) {
					editValue = originalValue;
					mainEditor.edSvc.CloseDropDown();
					return true;
				}
				return base.ProcessDialogKey(keyData);
			}
			protected void listBox_ItemCheckEventHandler(object sender, ItemCheckEventArgs e) {
				if(lockCheckUpdate != 0) return;
				BeginUpdate(); 
				try {
					string optionName = listBox.Items[e.Index].ToString();
					if(e.NewValue == CheckState.Checked) 
						EnableOption(optionName);
					else
						DisableOption(optionName);
					UpdateListBox();
				}
				finally {
					EndUpdate();
				}
			}
			void BeginUpdate() {
				lockCheckUpdate++;
			}
			void EndUpdate() {
				lockCheckUpdate --;
			}
			void SelectAll() {
				foreach(string optionName in listBox.Items) {
					if(optionName != noneOption) EnableOption(optionName);
				}
			}
			void ClearAll() {
				foreach(string optionName in listBox.Items) {
					if(optionName != noneOption) DisableOption(optionName);
				}
			}
			void UpdateListBox() {
				listBox.ListBoxBeginUpdate();
				try {
					for(int n = 0; n < listBox.Items.Count; n++) {
						string optionName = listBox.Items[n].ToString();
						listBox.SetItemChecked(n, IsOptionEnabled(optionName));
					}
				}
				finally {
					listBox.ListBoxEndUpdate();
				}
			}
			protected int GetOptionValue(string optionName) {
				Type t = EditValue.GetType();
				return (int)t.GetField(optionName).GetValue(EditValue);
			}
			protected void ConvertFromInt(int value) {
				Type t = EditValue.GetType();
				editValue = Activator.CreateInstance(t);
				t.GetFields()[0].SetValue(editValue, value);
			}
			protected void DisableOption(string optionName) {
				if(optionName == noneOption) {
					SelectAll();
					return;
				}
				ConvertFromInt((int)editValue & (~GetOptionValue(optionName)));
			}
			protected void EnableOption(string optionName) {
				if(optionName == noneOption) {
					ClearAll();
					return;
				}
				ConvertFromInt((int)editValue | GetOptionValue(optionName));
			}
			protected bool IsOptionEnabled(string optionName) {
				int opt = (int)editValue;
				if(optionName == noneOption) return opt == GetOptionValue(optionName);
				return (opt & GetOptionValue(optionName)) == GetOptionValue(optionName);
			}
			public object EditValue {
				get { return editValue; }
				set {
					if(editValue == value) return;
					originalValue = editValue = value;
					listBox.Items.Clear();
					ArrayList list = GetFields(editValue.GetType());
					BeginUpdate();
					try {
						foreach(System.Reflection.FieldInfo fi in list) {
							listBox.Items.Add(fi.Name, IsOptionEnabled(fi.Name) ? CheckState.Checked : CheckState.Unchecked);
						}
					}
					finally {
						EndUpdate();
					}
					int maxItems = Math.Min(listBox.Items.Count, 15);
					this.ClientSize = new Size(this.Size.Width,listBox.ItemHeight * maxItems);
				}
			}
			protected ArrayList GetFields(Type t) {
				ArrayList list = new ArrayList();
				System.Reflection.FieldInfo[] fields = t.GetFields();
				foreach(System.Reflection.FieldInfo field in fields) {
					if(field.IsSpecialName) continue;
					list.Add(field);
				}
				list.Sort(new FieldsComparer());
				return list;
			}
			const string noneOption = "None";
			class FieldsComparer : IComparer {
				int IComparer.Compare(object x, object y) {
					System.Reflection.FieldInfo fi1 = x as System.Reflection.FieldInfo;
					System.Reflection.FieldInfo fi2 = y as System.Reflection.FieldInfo;
					if(fi1 == null || fi2 == null) return Comparer.Default.Compare(x, y);
					if(fi1 == fi2) return 0;
					if(fi1.Name == noneOption) return -1;
					if(fi2.Name == noneOption) return 1;
					return Comparer.Default.Compare(fi1.Name, fi2.Name);
				}
			}
		}

	}
}
